#ifndef __PROTOCOL_DIST_HPP__
#define __PROTOCOL_DIST_HPP__
#include "winux.hpp"
#include "eienwebx.hpp"
#include "eiennet.hpp"

// 声明智能指针并获得引用。
#define PTR_REF(_ptr_, _ref_, _ctor_ ) winux::SimplePointer<_ctor_> _ptr_( new _ctor_() ); auto& _ref_ = *_ptr_.get(); 

typedef unsigned char uint8; // 0-255
typedef unsigned int uint;   // 0-255

class BinaryWriter
{
public:
    winux::GrowBuffer gBuf;
    inline void writeUint8(uint8 data)
    {
        gBuf.append(&data, sizeof(uint8));
    }
    inline void writeUint(uint data)
    {
        data = eiennet::htont(data);
        gBuf.append(&data, sizeof(uint));
    }
    inline void writeInt(int data)
    {
        data = eiennet::htont(data);
        gBuf.append(&data, sizeof(int));
    }
    inline void writeSizeT(size_t data)
    {
        data = eiennet::htont(data);
        gBuf.append(&data, sizeof(size_t));
    }
    inline void writeBool(bool data)
    {
        data = eiennet::htont(data);
        gBuf.append(&data, sizeof(bool));
    }
    inline void writeFloat(float data)
    {
        data = eiennet::htont(data);
        gBuf.append(&data, sizeof(float));
    }

    inline void writeString(std::string const &data)
    {
        uint len = static_cast<uint>(data.size());
        uint ntLen = eiennet::htont(len);
        gBuf.append(&ntLen, sizeof(uint));
        gBuf.append(data.c_str(), len * sizeof(uint8));
    }

    std::string getBinary()
    {
        return gBuf.toString<char>();
    }
};

class BinaryReader
{
public:
    std::string binStr;
    // 当前位置
    size_t seek = 0;
    BinaryReader(std::string const& binStr )
        : binStr(binStr)
    {
    }

    inline uint8 readUint8()
    {
        uint8 data = *(uint8 *)(binStr.c_str() + seek);
        seek += sizeof(uint8);
        return data;
    }
    inline size_t readSizeT()
    {
        size_t data = *(size_t *)(binStr.c_str() + seek);
        data = eiennet::ntoht(data);
        seek += sizeof(size_t);
        return data;
    }
    inline uint readUint()
    {
        uint data = *(uint *)(binStr.c_str() + seek);
        data = eiennet::ntoht(data);
        seek += sizeof(uint);
        return data;
    }
    inline int readInt()
    {
        int data = *(int *)(binStr.c_str() + seek);
        data = eiennet::ntoht(data);
        seek += sizeof(int);
        return data;
    }
    inline bool readBool()
    {
        bool data = *(bool *)(binStr.c_str() + seek);
        data = eiennet::ntoht(data);
        seek += sizeof(uint8);
        return data;
    }
    inline std::string readString()
    {
        uint len = this->readUint();
        std::string data;
        data.assign((binStr.c_str() + seek), len);
        seek += sizeof(uint8) * len;

        return data;
    }
    inline float readFloat()
    {
        float data;
        data = *(float *)(binStr.c_str() + seek);
        data = eiennet::ntoht(data);
        seek += sizeof(float);
        return data;
    }
};

class Protocol
{
protected:
    virtual void _toBinary(BinaryWriter& writer)=0;
    virtual void _fromBinary(BinaryReader& reader)=0;
public:
    std::string toBinary() {
        BinaryWriter writer;
        this->_toBinary(writer);
        return writer.getBinary();
    }
    void fromBinary(std::string const& input ) {
        BinaryReader reader(input);
        //跳过__cn
        reader.readString();
        this->_fromBinary( reader );
    }

    virtual const winux::String getClassName(){
        return "Protocol";
    }
    const winux::String toString()  {
        return this->toMixed().json();
    }

    virtual winux::Mixed toMixed()=0;
    virtual winux::Mixed toField() {
        winux::Mixed mix = this->toMixed();
        if( mix.has("__cn")){
            mix.del("__cn");
        }
        return mix;
    }
    virtual void fromMixed(winux::Mixed const& input)=0;
};
class ProtocolFactory
{
public:
    static winux::SimplePointer<Protocol> CreateFromBinary(winux::String const &binStr);
    static winux::SimplePointer<Protocol> CreateFromMixed(winux::Mixed const &input);
    static winux::SimplePointer<Protocol> CreateFromName(winux::String const &className);
};


//枚举

//消息协议

class ProtocolS2CIsLogged : public Protocol
{
public:
    bool is_logged = false;

    const winux::String getClassName(){
        return "S2CIsLogged";
    }
    
    winux::Mixed toMixed() {
        winux::Mixed out = winux::$c{};
        out["__cn"] = this->getClassName();
        out["is_logged"] = this->is_logged;

        return out;
    }
    void fromMixed(winux::Mixed const &input) {
        
        is_logged = static_cast<bool>(input.get("is_logged", is_logged ));

    }

    void _toBinary(BinaryWriter& writer) {
        
        writer.writeString(this->getClassName());
        int __arr_size = 0;
        writer.writeBool(this->is_logged);

    }
    void _fromBinary( BinaryReader& reader ) {
        
        int __arr_size = 0;
        is_logged = reader.readBool();

    }
};
class ProtocolObjectUserData : public Protocol
{
public:
     group_id = Guest;std::string name = "";int regtime = 0;int user_id = 0;

    const winux::String getClassName(){
        return "UserData";
    }
    
    winux::Mixed toMixed() {
        winux::Mixed out = winux::$c{};
        out["__cn"] = this->getClassName();
        out["group_id"] = this->group_id;
out["name"] = this->name;
out["regtime"] = this->regtime;
out["user_id"] = this->user_id;

        return out;
    }
    void fromMixed(winux::Mixed const &input) {
        
        group_id = static_cast<>(input.get("group_id", group_id ));
name = static_cast<std::string>(input.get("name", name ));
regtime = static_cast<int>(input.get("regtime", regtime ));
user_id = static_cast<int>(input.get("user_id", user_id ));

    }

    void _toBinary(BinaryWriter& writer) {
        
        writer.writeString(this->getClassName());
        int __arr_size = 0;
        writer.(this->group_id);
writer.writeString(this->name);
writer.writeInt(this->regtime);
writer.writeInt(this->user_id);

    }
    void _fromBinary( BinaryReader& reader ) {
        
        int __arr_size = 0;
        group_id = reader.();
name = reader.readString();
regtime = reader.readInt();
user_id = reader.readInt();

    }
};
class ProtocolObjectUserDataField : public Protocol
{
public:
     group_id = Guest;std::string name = "";int regtime = 0;int user_id = 0;std::string password = "";

    const winux::String getClassName(){
        return "UserDataField";
    }
    
    winux::Mixed toMixed() {
        winux::Mixed out = winux::$c{};
        out["__cn"] = this->getClassName();
        out["group_id"] = this->group_id;
out["name"] = this->name;
out["regtime"] = this->regtime;
out["user_id"] = this->user_id;
out["password"] = this->password;

        return out;
    }
    void fromMixed(winux::Mixed const &input) {
        
        group_id = static_cast<>(input.get("group_id", group_id ));
name = static_cast<std::string>(input.get("name", name ));
regtime = static_cast<int>(input.get("regtime", regtime ));
user_id = static_cast<int>(input.get("user_id", user_id ));
password = static_cast<std::string>(input.get("password", password ));

    }

    void _toBinary(BinaryWriter& writer) {
        
        writer.writeString(this->getClassName());
        int __arr_size = 0;
        writer.(this->group_id);
writer.writeString(this->name);
writer.writeInt(this->regtime);
writer.writeInt(this->user_id);
writer.writeString(this->password);

    }
    void _fromBinary( BinaryReader& reader ) {
        
        int __arr_size = 0;
        group_id = reader.();
name = reader.readString();
regtime = reader.readInt();
user_id = reader.readInt();
password = reader.readString();

    }
};
class ProtocolS2CUserData : public Protocol
{
public:
    winux::SimplePointer<ProtocolObjectUserData> user_data = winux::MakeSimple( new ProtocolObjectUserData());;

    const winux::String getClassName(){
        return "S2CUserData";
    }
    
    winux::Mixed toMixed() {
        winux::Mixed out = winux::$c{};
        out["__cn"] = this->getClassName();
        out["user_data"] = this->user_data->toMixed();

        return out;
    }
    void fromMixed(winux::Mixed const &input) {
        
        if( input.has("user_data") && !input["user_data"].isNull()) {
        user_data->fromMixed(input.get("user_data", winux::Mixed() ));
        // user_data = ProtocolFactory::CreateFromMixed( input.get("user_data", winux::Mixed() )).cast<ProtocolObjectUserData>();
    }

    }

    void _toBinary(BinaryWriter& writer) {
        
        writer.writeString(this->getClassName());
        int __arr_size = 0;
        writer.writeString(this->user_data->toBinary());

    }
    void _fromBinary( BinaryReader& reader ) {
        
        int __arr_size = 0;
        user_data->fromBinary(reader.readString());

    }
};
class ProtocolC2SReg : public Protocol
{
public:
    std::string user_name = "";std::string invite_code = "";std::string user_pwd = "";int is_author = 0;

    const winux::String getClassName(){
        return "C2SReg";
    }
    
    winux::Mixed toMixed() {
        winux::Mixed out = winux::$c{};
        out["__cn"] = this->getClassName();
        out["user_name"] = this->user_name;
out["invite_code"] = this->invite_code;
out["user_pwd"] = this->user_pwd;
out["is_author"] = this->is_author;

        return out;
    }
    void fromMixed(winux::Mixed const &input) {
        
        user_name = static_cast<std::string>(input.get("user_name", user_name ));
invite_code = static_cast<std::string>(input.get("invite_code", invite_code ));
user_pwd = static_cast<std::string>(input.get("user_pwd", user_pwd ));
is_author = static_cast<int>(input.get("is_author", is_author ));

    }

    void _toBinary(BinaryWriter& writer) {
        
        writer.writeString(this->getClassName());
        int __arr_size = 0;
        writer.writeString(this->user_name);
writer.writeString(this->invite_code);
writer.writeString(this->user_pwd);
writer.writeInt(this->is_author);

    }
    void _fromBinary( BinaryReader& reader ) {
        
        int __arr_size = 0;
        user_name = reader.readString();
invite_code = reader.readString();
user_pwd = reader.readString();
is_author = reader.readInt();

    }
};
class ProtocolS2CReg : public Protocol
{
public:
    winux::SimplePointer<ProtocolObjectUserData> user_data = winux::MakeSimple( new ProtocolObjectUserData());;

    const winux::String getClassName(){
        return "S2CReg";
    }
    
    winux::Mixed toMixed() {
        winux::Mixed out = winux::$c{};
        out["__cn"] = this->getClassName();
        out["user_data"] = this->user_data->toMixed();

        return out;
    }
    void fromMixed(winux::Mixed const &input) {
        
        if( input.has("user_data") && !input["user_data"].isNull()) {
        user_data->fromMixed(input.get("user_data", winux::Mixed() ));
        // user_data = ProtocolFactory::CreateFromMixed( input.get("user_data", winux::Mixed() )).cast<ProtocolObjectUserData>();
    }

    }

    void _toBinary(BinaryWriter& writer) {
        
        writer.writeString(this->getClassName());
        int __arr_size = 0;
        writer.writeString(this->user_data->toBinary());

    }
    void _fromBinary( BinaryReader& reader ) {
        
        int __arr_size = 0;
        user_data->fromBinary(reader.readString());

    }
};
class ProtocolC2SLogin : public Protocol
{
public:
    std::string username = "";std::string password = "";

    const winux::String getClassName(){
        return "C2SLogin";
    }
    
    winux::Mixed toMixed() {
        winux::Mixed out = winux::$c{};
        out["__cn"] = this->getClassName();
        out["username"] = this->username;
out["password"] = this->password;

        return out;
    }
    void fromMixed(winux::Mixed const &input) {
        
        username = static_cast<std::string>(input.get("username", username ));
password = static_cast<std::string>(input.get("password", password ));

    }

    void _toBinary(BinaryWriter& writer) {
        
        writer.writeString(this->getClassName());
        int __arr_size = 0;
        writer.writeString(this->username);
writer.writeString(this->password);

    }
    void _fromBinary( BinaryReader& reader ) {
        
        int __arr_size = 0;
        username = reader.readString();
password = reader.readString();

    }
};
class ProtocolS2CLogin : public Protocol
{
public:
    winux::SimplePointer<ProtocolObjectUserData> user_data = winux::MakeSimple( new ProtocolObjectUserData());;

    const winux::String getClassName(){
        return "S2CLogin";
    }
    
    winux::Mixed toMixed() {
        winux::Mixed out = winux::$c{};
        out["__cn"] = this->getClassName();
        out["user_data"] = this->user_data->toMixed();

        return out;
    }
    void fromMixed(winux::Mixed const &input) {
        
        if( input.has("user_data") && !input["user_data"].isNull()) {
        user_data->fromMixed(input.get("user_data", winux::Mixed() ));
        // user_data = ProtocolFactory::CreateFromMixed( input.get("user_data", winux::Mixed() )).cast<ProtocolObjectUserData>();
    }

    }

    void _toBinary(BinaryWriter& writer) {
        
        writer.writeString(this->getClassName());
        int __arr_size = 0;
        writer.writeString(this->user_data->toBinary());

    }
    void _fromBinary( BinaryReader& reader ) {
        
        int __arr_size = 0;
        user_data->fromBinary(reader.readString());

    }
};
class ProtocolC2SLogout : public Protocol
{
public:
    

    const winux::String getClassName(){
        return "C2SLogout";
    }
    
    winux::Mixed toMixed() {
        winux::Mixed out = winux::$c{};
        out["__cn"] = this->getClassName();
        
        return out;
    }
    void fromMixed(winux::Mixed const &input) {
        
        
    }

    void _toBinary(BinaryWriter& writer) {
        
        writer.writeString(this->getClassName());
        int __arr_size = 0;
        
    }
    void _fromBinary( BinaryReader& reader ) {
        
        int __arr_size = 0;
        
    }
};
class ProtocolS2CLogout : public Protocol
{
public:
    bool ok = false;

    const winux::String getClassName(){
        return "S2CLogout";
    }
    
    winux::Mixed toMixed() {
        winux::Mixed out = winux::$c{};
        out["__cn"] = this->getClassName();
        out["ok"] = this->ok;

        return out;
    }
    void fromMixed(winux::Mixed const &input) {
        
        ok = static_cast<bool>(input.get("ok", ok ));

    }

    void _toBinary(BinaryWriter& writer) {
        
        writer.writeString(this->getClassName());
        int __arr_size = 0;
        writer.writeBool(this->ok);

    }
    void _fromBinary( BinaryReader& reader ) {
        
        int __arr_size = 0;
        ok = reader.readBool();

    }
};

//消息工厂
winux::SimplePointer<Protocol> ProtocolFactory::CreateFromBinary(winux::String const& binStr)
{
    BinaryReader reader( binStr );
    winux::String className = reader.readString();
    winux::SimplePointer<Protocol> ptr = ProtocolFactory::CreateFromName(className);
    ptr->fromBinary( binStr );
    return ptr;
}
winux::SimplePointer<Protocol> ProtocolFactory::CreateFromMixed(winux::Mixed const& input )
{
    winux::SimplePointer<Protocol> ptr = ProtocolFactory::CreateFromName( input["__cn"]);
    ptr->fromMixed( input );
    return ptr;
}
winux::SimplePointer<Protocol> ProtocolFactory::CreateFromName(winux::String const& className )
{
    if(0){
    }
    else if( className == "S2CIsLogged") {
            return winux::SimplePointer<Protocol>(new ProtocolS2CIsLogged());
        }else if( className == "UserData") {
            return winux::SimplePointer<Protocol>(new ProtocolObjectUserData());
        }else if( className == "UserDataField") {
            return winux::SimplePointer<Protocol>(new ProtocolObjectUserDataField());
        }else if( className == "S2CUserData") {
            return winux::SimplePointer<Protocol>(new ProtocolS2CUserData());
        }else if( className == "C2SReg") {
            return winux::SimplePointer<Protocol>(new ProtocolC2SReg());
        }else if( className == "S2CReg") {
            return winux::SimplePointer<Protocol>(new ProtocolS2CReg());
        }else if( className == "C2SLogin") {
            return winux::SimplePointer<Protocol>(new ProtocolC2SLogin());
        }else if( className == "S2CLogin") {
            return winux::SimplePointer<Protocol>(new ProtocolS2CLogin());
        }else if( className == "C2SLogout") {
            return winux::SimplePointer<Protocol>(new ProtocolC2SLogout());
        }else if( className == "S2CLogout") {
            return winux::SimplePointer<Protocol>(new ProtocolS2CLogout());
        }
    return winux::SimplePointer<Protocol>();
}

#endif //__PROTOCOL_DIST_HPP__